<template>
  <div class="doc-container">

    <div class="left">

      <div class="namespace">
        <a-row>
          切换命名空间
          <a-select v-model="namespaceId" style="width: 130px" @change="handleNamespaceChange">
            <a-select-option v-for="(item, index) in namespaceList"
                             :key="index" :value="item.id">
              {{ item.name }}
            </a-select-option>
          </a-select>
          <a-icon @click="showSettingModel = true" style="margin-left: 10px; font-size: 20px; color: blue"
                  type="setting"/>
        </a-row>
      </div>
      <div class="search">
        <a-input-search
            v-model:value="keyword"
            placeholder="请输入关键字搜索"
            style="width: 190px; margin-top: 10px; margin-right: 10px"
        />
        <a-dropdown :trigger="['click']">
          <a class="ant-dropdown-link" @click="e => e.preventDefault()">
            <a-button class="upload-btn" type="primary">+</a-button>
          </a>
          <a-menu slot="overlay">
            <a-menu-item key="0">
              <a @click="handleToCreateNamespace" href="#">新建命名空间</a>
            </a-menu-item>
            <a-menu-item key="1">
              <a @click="uploadBizModalVisible = true" href="#">上传 biz</a>
            </a-menu-item>
          </a-menu>
        </a-dropdown>
      </div>
      <div class="tree">
        <a-tree
            @select="handleOnTreeNode"
            :defaultExpandAll="true"
            tree-default-expand-all
            :tree-data="treeData"
            :expandedKeys="expandedData"
            @expand="handleExpand"
        >
          <!-- 插槽名称写死为custom,需要传入的treeData中需要用到自定义插槽的对象中增加scopedSlots: { title: "custom" }属性 -->
          <template slot="custom" slot-scope="item">
            <div class="tree-custom-item-edit-input" v-if="item.isEdit">
              <a-input ref="new" type="text" class="editInput" v-model.trim="newItem"
                       @pressEnter="addOrEditConfirm(item)"/>
              <CloseCircleOutlined style="margin-left: 5px" @click="addCancel(item)"/>
              <CheckCircleOutlined style="margin-left: 3px" @click="addOrEditConfirm(item)"/>
            </div>
            <div :style="{ color: item.type === 1 ? '#1890ff' : '' }" class="tree-item-btn" v-else>
              <a-icon style="margin-right: 5px" v-show="item.type === 0" type="folder-open"/>
              <a-tag v-if="item.type === 1" color="#108ee9">
                POST
              </a-tag>
              <span class="node-title">{{ item.title }}</span>
              <span v-show="item.type === 0" class="icon-wrap">
                  <a-dropdown>
                      <a class="ant-dropdown-link" @click="e => e.preventDefault()">
                        <PlusOutlined/>
                      </a>
                      <a-menu @click="onDropdownClick($event, item)" slot="overlay">
                        <a-menu-item :key="0">
                          <PlusOutlined/> 新增分组
                        </a-menu-item>
                        <a-menu-item :key="1">
                          <PlusOutlined/> 新增接口
                        </a-menu-item>
                      </a-menu>
                  </a-dropdown>
              </span>
              <span v-show="item.type === 0" class="icon-wrap">
                <EditOutlined @click="editItem(item)"/>
              </span>
              <span class="icon-wrap">
                 <a-popconfirm
                     placement="right"
                     title="确认要删除该接口吗?"
                     ok-text="Yes"
                     cancel-text="No"
                     @confirm="deleteItem(item)"
                 >
                   <DeleteOutlined/>
                </a-popconfirm>
              </span>
            </div>
          </template>
        </a-tree>
      </div>

    </div>

    <div class="right">
      <div class="right-content">
        <div v-if="panes.length > 0" class="tab">
          <a-tabs @edit="handleTabRemove"
                  @change="handleTabChange"
                  v-model:activeKey="topTabActiveKey"
                  hide-add
                  type="editable-card">
            <a-tab-pane v-for="pane in panes" :key="pane.key" :tab="pane.title" :closable="pane.closable">
              {{ pane.content }}
            </a-tab-pane>
          </a-tabs>
        </div>
        <div class="send-method">
          <a-input-group compact>
            <a-select @change="handleSendMethodChange" style="width: 100px" v-model:value="sendMethod">
              <a-select-option v-for="item in sendMethodOptions" :value="item">{{ item }}</a-select-option>
            </a-select>
            <a-input style="width: 70%; border-radius: 4px" v-model:value="curRequest.url"/>
            <a-button @click="$message.error('抱歉，不在服务区~')"
                      style="margin-left: 12px; border-radius: 4px" type="primary">发送
            </a-button>
            <a-button @click="copy(curRequest.url)" style="margin-left: 12px; border-radius: 4px">复制</a-button>
          </a-input-group>
          <a-alert class="sendName" :message="!!curRequest ? curRequest.name : '接口名称' " type="info"/>
        </div>
        <div class="param-tab">
          <a-tabs v-model:activeKey="activeKey">
            <a-tab-pane key="1" tab="Params(5)">1</a-tab-pane>
            <a-tab-pane key="2" tab="Body" force-render>
              <RequestJsonBodyView/>
            </a-tab-pane>
            <a-tab-pane key="3" tab="Headers">3</a-tab-pane>
            <a-tab-pane key="4" tab="Cookies">4</a-tab-pane>
            <a-tab-pane key="5" tab="Auth">5</a-tab-pane>
            <a-tab-pane key="6" tab="前置操作">6</a-tab-pane>
            <a-tab-pane key="7" tab="后置操作">7</a-tab-pane>
            <a-tab-pane key="8" tab="设置">8</a-tab-pane>
          </a-tabs>
        </div>
      </div>
    </div>

    <a-modal width="80%" :footer="false" v-model:visible="uploadBizModalVisible" title="上传 biz">
      <a-upload-dragger
          style="margin-top: 10px;"
          type="drag"
          :before-upload="handleBeforeUpload"
          :customRequest="handleImportDocument"
          :on-success="handleSuccess"
          :fileList="fileList"
      >
        <div style="padding: 20px 0; height: 300px">
          <Icon type="ios-cloud-upload" size="52" style="color: #3399ff"></Icon>
          <p>请点击或拖曳 biz 文件到这里进行上传</p>
        </div>
      </a-upload-dragger>
    </a-modal>

    <a-modal @ok="handleCreateNamespaceOk" v-model="showCreateNamespaceModel" title="新建命名空间">
      <a-input placeholder="请输入命名空间名称" v-model="namespace.name"/>
    </a-modal>

    <a-modal width="80%" v-model="showSettingModel" title="设置" @cancel="showSettingModel = false"
             @ok="showSettingModel = false">
      <Setting @load="handleSelectNameSpaces"/>
    </a-modal>

  </div>
</template>

<script>

import vueJsonEditor from 'vue-json-editor'
import {
  CloseCircleOutlined,
  CheckCircleOutlined,
  PlusOutlined,
  EditOutlined,
  DeleteOutlined
} from "@ant-design/icons-vue";
import {nanoid} from "nanoid";
import EditableCell from "@/components/EditableCell.vue";
import Setting from "@/components/Setting.vue";
import RequestJsonBodyView from "@/components/RequestJsonBodyView.vue";
import { copy } from "@/utils";

export default {
  name: "Doc",
  components: {
    RequestJsonBodyView,
    Setting,
    EditableCell,
    vueJsonEditor,
    CloseCircleOutlined,
    CheckCircleOutlined,
    PlusOutlined,
    EditOutlined,
    DeleteOutlined
  },
  data() {
    return {
      showSettingModel: false,
      namespace: {
        name: ''
      },
      showCreateNamespaceModel: false,
      namespaceList: [],
      inputColumns: [
        {
          align: 'left',
          title: '属性',
          dataIndex: 'fieldName',
          key: 'fieldName',
          width: '160px',
          scopedSlots: {customRender: 'fieldName'}
        },
        {
          align: 'center',
          title: '是否必输',
          dataIndex: 'required',
          key: 'required',
          width: '100px',
          scopedSlots: {customRender: 'required'}
        },
        {
          align: 'center',
          title: '备注',
          dataIndex: 'fieldComment',
          width: '50%',
          key: 'fieldComment',
          scopedSlots: {customRender: 'comment'}
        },
      ],
      outputColumns: [
        {
          align: 'left',
          title: '属性',
          dataIndex: 'fieldName',
          key: 'fieldName',
          width: '160px',
          scopedSlots: {customRender: 'fieldName'}
        },
        {
          align: 'center',
          title: '备注',
          dataIndex: 'fieldComment',
          width: '50%',
          key: 'fieldComment',
          scopedSlots: {customRender: 'comment'}
        },
      ],
      sendMethodColor: {
        'GET': '#2db7f5',
        'POST': '#108ee9',
        'PUT': '#87d068',
        'DELETE': '#f50',
      },
      uploadBizModalVisible: false,
      tmpArr: [],
      radioValue: 4,
      tempDocData: [],
      idLabelList: [],
      docData: [],
      openKeys: ['sub1'],
      selectedKeys: [],
      keyword: '',
      sendMethodOptions: ['GET', 'POST', 'PUT', 'DELETE'],
      sendMethod: 'POST',
      panes: [],
      topTabActiveKey: '',
      activeKey: '2',
      newTabIndex: 0,
      curRequest: {
        name: '',
        url: ''
      },
      treeData: [],
      expandedData: [],
      newFlag: 'edit',
      editFlag: false,
      newItem: '',
      uploadFile: null,
      fileList: [],
      namespaceId: '',
      treeId: '',
    }
  },
  created() {
    // 获取左侧树形数据
    this.handleSelectTreeData()
    // 获取命名空间数据
    this.handleSelectNameSpaces()
  },
  methods: {
    copy,
    // 创建新命名空间
    handleToCreateNamespace() {
      this.namespace.name = ''
      this.showCreateNamespaceModel = true
    },
    // 新建命名空间
    handleCreateNamespaceOk() {
      this.$axios.post('http://localhost:20220/nameSpace/insert', this.namespace).then(res => {
        if (200 === res.data.code) {
          this.namespaceId = res.data.data
          this.showCreateNamespaceModel = false
          this.$message.success('新建空间成功!')
          this.handleSelectNameSpaces()
        }
      })
    },
    // 点击树形事件
    handleOnTreeNode(selectedKeys, info) {
      console.log('========= selectedKeys =========');
      console.log(selectedKeys[0])
      if (!!selectedKeys[0]) {
        this.treeId = selectedKeys[0]
        // 如果这个 treeId 是 requestId 的话, 就取 request 中的 treeId
        this.$axios.get('http://localhost:20220/request/get/' + this.treeId).then(res => {
          if (200 === res.data.code && null !== res.data.data) {
            this.treeId = res.data.data.treeId
            let e = {key: selectedKeys[0]}
            // 更新 tab 等
            this.handleClickRequest(e)
          }
        })
      }
    },
    // 切换命名空间
    handleNamespaceChange(val) {
      this.namespaceId = val
      this.handleSelectTreeData()
    },
    // 获取命名空间数据
    handleSelectNameSpaces() {
      this.$axios.get('http://localhost:20220/nameSpace/selectList').then(res => {
        if (200 === res.data.code) {
          this.namespaceList = res.data.data
        }
      })
    },
    handleBeforeUpload(data) {
      this.uploadFile = data
      this.fileList = []
      this.fileList.push(data)
    },
    handleImportDocument() {
      var formData = new FormData();
      formData.append('file', this.uploadFile)
      formData.append('namespaceId', this.namespaceId)
      formData.append('treeId', this.treeId)
      this.$axios.post('http://localhost:20220/importDocument', formData).then(res => {
        this.$message.success('接口文档生成成功!')
        this.uploadBizModalVisible = false
        this.fileList = []
        this.handleSelectTreeData()
      })
    },
    onDropdownClick({key}, item) {
      this.addItem(item, key)
    },
    addItem(item, type) {
      if (this.editFlag) {
        this.$message.warning('已有编辑项，请先编辑完成！')
        return
      }
      let o = {
        children: [],
        key: nanoid(),
        title: '',
        level: item.level + 1,
        scopedSlots: {title: "custom"},
        isEdit: true,
        isAdd: true,
        parentId: item.key,
        type: type
      }
      item.children.push(o)
      this.newItem = ''
      this.editFlag = true
      this.newFlag = true
      this.isAdd = true
      this.expandedData.push(item.key)
      this.$nextTick(() => {
        this.$refs.new.focus()
      })
    },
    editItem(item) {
      if (this.editFlag) {
        this.$message.warning('已有编辑项，请先编辑完成！')
        return
      }
      item.isEdit = true
      this.newFlag = false
      this.editFlag = true
      this.newItem = item.title
      this.treeListItemActions(this.treeData, item.key, 'edit', item)
      this.$nextTick(() => {
        this.$refs.new.focus()
      })
    },
    deleteItem(item) {
      if (item.children.length > 0) {
        let that = this
        this.$confirm({
          title: '操作提示',
          content: '将会删除此节点的所有子节点，确定删除?',
          centered: true,
          onOk() {
            that.treeListItemActions(that.treeData, item.key, 'delete')
            // 删除
            var formData = new FormData();
            formData.append('id', item.key)
            formData.append('type', item.type)
            this.$axios.post('http://localhost:20220/treeSide/delete', formData).then(res => {
              if (200 === res.data.code) {
                this.$message.success('删除成功!')
              }
            })
          },
          onCancel() {
            that.$message.info('已取消')
          }
        })
      } else {
        this.treeListItemActions(this.treeData, item.key, 'delete')
        // 删除
        var formData = new FormData();
        formData.append('id', item.key)
        formData.append('type', item.type)
        this.$axios.post('http://localhost:20220/treeSide/delete', formData).then(res => {
          if (200 === res.data.code) {
            this.$message.success('删除成功!')
          }
        })
      }
    },
    handleExpand(expandedKeys) {
      this.expandedData = expandedKeys
    },
    addCancel(item) {
      this.newItem = ''
      item.isEdit = false
      this.editFlag = false
      this.treeListItemActions(this.treeData, item.key, this.newFlag ? 'delete' : 'edit', item)
    },
    addOrEditConfirm(item) {
      if (this.newItem !== null && this.newItem.trim() !== '') {
        item.title = this.newItem
        item.isEdit = false
        this.editFlag = false
        this.treeListItemActions(this.treeData, item.key, 'edit', item)
        console.log(item)
        let submitObj = {
          ...item,
          id: item.key,
          name: item.title,
          parentId: item.parentId,
          namespaceId: this.namespaceId
        }
        if (item.isAdd) {
          // 新增
          if (item.type === 0) {
            this.$axios.post('http://localhost:20220/treeSide/insert', submitObj).then(res => {
              if (200 === res.data.code) {
                this.$message.success('新增成功!')
                this.handleSelectTreeData()
              }
            })
          } else {
            // this.$axios.post('http://localhost:20220/request/insert', submitObj).then(res => {
            //   if (200 === res.data.code) {
            this.$message.success('新增接口成功!')
            this.handleSelectTreeData()
            //   }
            // })
          }
        } else {
          // 更新
          this.$axios.put('http://localhost:20220/treeSide/update', submitObj).then(res => {
            if (200 === res.data.code) {
              this.$message.success('更新成功!')
              this.handleSelectTreeData()
            }
          })
        }
      }
    },
    // 根据id属性从数组（树结构）中匹配元素，执行action操作
    treeListItemActions(treeList, key, action, item) {
      if (!treeList || !treeList.length) {
        return
      }
      for (let i = 0; i < treeList.length; i++) {
        if (treeList[i].key === key) {
          if (action === 'delete') {
            treeList.splice(i, 1)
            this.$forceUpdate()
          } else if (action === 'edit') {
            treeList[i] = item
            this.$forceUpdate()
          }
          break
        }
        this.treeListItemActions(treeList[i].children, key, action, item)
      }
    },
    handleSelectTreeData() {
      this.treeData = []
      this.$axios.get('http://localhost:20220/treeSide/treeData?namespaceId=' + this.namespaceId).then(res => {
        if (res.data && res.data.length > 0) {
          console.log(res.data)

          const filterChildren = (arr = []) => {
            return arr.map(item => {
              return {
                isEdit: false,
                isNewNode: false,
                title: item.name,
                key: item.id,
                parentId: item.parentId,
                children: !!item.children ? filterChildren(item.children) : [],
                scopedSlots: {title: "custom"},
                requestId: item.requestId,
                type: item.type
              }
            })
          }

          this.treeData = filterChildren(res.data)
          console.log(this.treeData)
          // 设置默认展开的 key
          this.expandedData = this.treeData.map(item => item.key)
        }
      })
    },
    // 监听复制
    handleCopy(row) {
      console.log(row.fieldName)
      copy(row.fieldName)
    },
    // 监听发送方法
    handleSendMethodChange(val) {
      // 更新左侧菜单请求类型
      this.$set(this.curRequest, 'sendMethod', val)
      this.$set(this.curRequest, 'sendMethodColor', this.sendMethodColor[val])
    },
    // 监听顶部 tab 移除
    handleTabRemove(key) {
      let index = this.panes.findIndex(item => item.key === key)
      if (this.panes.length > 1) {
        this.panes.splice(index, 1)
        // 先更新当前请求数据记录对象
        let e = {key: this.panes[index - 1].key}
        // 左侧菜单点击, 查询 request
        this.handleClickRequest(e)
      }
    },
    // 监听顶部 tab 变化
    handleTabChange(key) {
      this.topTabActiveKey = key
      console.log(key)
      // 激活左侧菜单状态
      // this.selectedKeys = [key]
      // 更新当前请求数据
      let e = {key: key}
      this.handleClickRequest(e)
    },
    // 文件上传成功
    handleSuccess(res, file) {
      this.docData = res
      this.tempDocData = res
      this.idLabelList = res[0].idLabelList
      // 重置请求类型
      this.sendMethod = 'POST'
      this.uploadBizModalVisible = false
    },
    // 监听行编辑
    onCellChange(key, dataIndex, value) {
      console.log(key, dataIndex, value)
    },
    // 左侧菜单点击, 查询 request
    handleClickRequest(e) {
      console.log('handleClickRequest', e);
      this.$axios.get('http://localhost:20220/request/get/' + e.key).then(res => {
        if (200 === res.data.code && null !== res.data.data) {
          // 先更新当前请求数据记录对象
          this.curRequest = res.data.data
          // 更新 tab
          let find = this.panes.find(item => item.key === e.key);
          if (!find) {
            this.panes.push({title: this.curRequest.name, content: '', key: e.key, closable: true})
          }
          // 激活顶部请求页签 tab
          this.topTabActiveKey = e.key
          // 左侧菜单点击, 查询 request
          eventBus.$emit('getRequestInfo', res.data.data)
        }
      })
    },
  },
  watch: {
    namespaceId(val) {
      if (val) {
        this.handleSelectTreeData()
      }
    },
    keyword(val) {
      if (val) {
        let arr = JSON.parse(JSON.stringify(this.tempDocData))
        this.docData = arr.filter(item => item.name.includes(val))
        console.log(this.docData)
      } else {
        this.docData = this.tempDocData
      }
    }
  },
}

</script>

<style lang="less" scoped>

::v-deep .ant-modal-close-x {
  color: white;
}

::v-deep .ant-modal-title {
  color: white;
}

::v-deep .anticon svg {
  cursor: default;
}

::v-deep .ant-table table {
  cursor: pointer;
}

::v-deep .ant-modal-header {
  background: #50a2f8;
  border-radius: 16px 16px 0 0;
  padding: 12px 24px;
}

::v-deep .ant-modal-content {
  border-radius: 16px;
}

::v-deep .ivu-upload-drag {
  height: 100%;
}

.doc-container {
  display: flex;
  width: 100%;
  height: calc(100vh - 20px);

  .left {
    display: flex;
    flex-direction: column;
    align-items: center;
    width: 255px;
    height: 100%;

    .search {
      display: flex;

      .upload-btn {
        margin-top: 10px;
        font-size: 16px;
      }
    }

    .tree {
      width: 100%;
    }

    .side-menu {
      overflow-y: scroll;
      height: calc(100vh - 60px);
    }

    .side-menu::-webkit-scrollbar {
      width: 0;
    }

  }

  .right {
    width: calc(100vw - 255px);
    height: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;

    .right-content {
      width: 100%;
      height: 540px;
      overflow-y: scroll;

      .tab {
        width: 96%;
        height: 50px;
      }

      .send-method {
        width: 96%;
        height: 80px;

        .sendName {
          margin-top: 8px;
        }
      }

      .param-tab {
        width: 96%;
        height: 60px;

        .json-body-wrap {
          width: 100%;

          .radio-group {
            width: 100%;
            height: 40px;
          }

          .json-radio-group {
            margin: auto;
            width: max-content;
            height: 50px;
          }

          .json-panel {
            width: 100%;
            height: 300px;
          }

          .json-output {
            margin-top: 20px;
          }

        }

      }

    }

  }

  //.right::-webkit-scrollbar {
  //  width: 0;
  //}

  ::v-deep .jsoneditor-vue {
    height: 300px;
  }

  ::v-deep .jsoneditor-poweredBy {
    display: none;
  }

}


.editInput {
  width: 120px;
  margin-left: -4px;
}

.tree-custom-item-edit-input {
  height: 32px;
}

::v-deep .ant-tree li .ant-tree-node-content-wrapper.ant-tree-node-selected {
  background: none;
}

.icon-wrap {
  margin-left: 5px;
  color: #1890ff;
}

.namespace {
  width: 100%;
  height: 36px;
}

</style>